﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Collections;
using System.Net.Sockets;

namespace BaseHelper.Http
{
     public class HttpServerHelper
    {
        public delegate void RequestHandler(string remoteIP, HttpListenerResponse response, string domain,string url, SortedList<string, string> cookies, SortedList<string, string> fields, SortedList<string, byte[]> binarys);
        public static RequestHandler RequestEvent = null;
        private static HttpListener listener;
        private static Thread thListener;

        public static SortedList<string, string> StaticDefine = new SortedList<string, string>();
        public static string RootFolderStream;  //资源文件内嵌数据
        public static string RootFolder;        //资源文件硬盘目录
        public static string TempFolder;        //临时换成文件夹
        public static string DefaultPage;       //默认首页文件名
        public static string Error;             //错误信息
        public static string TargetUrl;         //访问地址
        public static int Port;                 //端口
        public static Assembly AssemblyGet;


        private static SortedList<string, string> _headContentType = new SortedList<string, string>();
        private static string analyzeLeft = "{%";
        private static string analyzeRight = "%}";

        public static bool IsRun
        {
            get
            {
                return listener != null && listener.IsListening;
            }
        }

        public static bool Start()
        {
            if (!HttpListener.IsSupported)
            {
                return false;
            }
            if (_headContentType.Count == 0)
            {
                _headContentType.Add("js", "application/x-javascript");
                _headContentType.Add("rtf", "application/rtf");
                _headContentType.Add("exe", "application/x-msdownload");
                _headContentType.Add("css", "text/css");
                _headContentType.Add("svg", "image/svg+xml ");
                _headContentType.Add("html", "text/html");
                _headContentType.Add("htm", "text/html");
                _headContentType.Add("png", "image/png");
                _headContentType.Add("cur", "image/cur");
                _headContentType.Add("bmp", "image/bmp");
                _headContentType.Add("gif", "image/gif");
                _headContentType.Add("jpg", "image/jpeg");
                _headContentType.Add("mp3", "audio/mp3");
                _headContentType.Add("ico", "image/ico");
                _headContentType.Add("zip", "application/octet-stream");
                _headContentType.Add("csv", "application/csv");

                _headContentType.Add("woff", "font/x-font-woff");
                _headContentType.Add("woff2", "font/x-font-woff");

                _headContentType.Add("otf", "application/x-font-opentype");
                _headContentType.Add("eot", "application/vnd.ms-fontobject");
                _headContentType.Add("ttf", "application/x-font-truetype");



            }

            try
            {
                Error = "";
                listener = new HttpListener();
                listener.Prefixes.Add("http://localhost:" + Port + "/");
                listener.Prefixes.Add("http://127.0.0.1:" + Port + "/");


                TargetUrl = "";
                IPAddress[] ipadrlist = Dns.GetHostAddresses(Dns.GetHostName());
                foreach (IPAddress ip in ipadrlist)
                {
                    if (ip.AddressFamily == AddressFamily.InterNetwork)
                    {
                        listener.Prefixes.Add("http://" + ip + ":" + Port + "/");
                        if (Port == 80)
                            TargetUrl += "http://" + ip + "、";
                        else
                            TargetUrl += "http://" + ip + ":" + Port + "/、";
                    }
                }

                listener.Start();
                listener.BeginGetContext(new AsyncCallback(GetContextCallBack), listener);

                // 3.12  暂时改成异步的，看下效果
                //thListener = new Thread(new ThreadStart(thListen));
                //thListener.Name = "Helper.Http.HttpServerHelper.Start().thListener";
                //thListener.SetApartmentState(ApartmentState.STA);
                //thListener.Start();
            }
            catch (Exception ex)
            {
                Error = ex.Message;
                return false;
            }
            return true;
        }

        public static void Stop()
        {
            try
            {
                if (listener != null)
                {
                    listener.Stop();
                    listener.Abort();
                    listener.Close();
                    listener = null;
                }
            }
            catch { }
            try
            {

                if (thListener != null)
                    thListener.Abort();
                thListener = null;
            }
            catch { }

        }

        private static void GetContextCallBack(IAsyncResult ar)
        {
            try
            {
                listener = ar.AsyncState as HttpListener;
                HttpListenerContext context = listener.EndGetContext(ar);
                listener.BeginGetContext(new AsyncCallback(GetContextCallBack), listener);

                thContext(context);
                //Thread th = new Thread(new ParameterizedThreadStart(thContext));
                //th.Name = "HttpServerHelper.GetContextCallBack()";
                //th.SetApartmentState(ApartmentState.STA);
                //th.Start(context);
            }
            catch (Exception ex)
            {
                try
                {
                    if (listener != null)
                    {
                        if (!listener.IsListening)
                            listener.Start();
                        listener.BeginGetContext(new AsyncCallback(GetContextCallBack), listener);
                    }

                }
                catch (Exception ex2)
                {
                    //LogHelper.Err("Helper.Http.HttpServerHelper.cs", "GetContextCallBack()", ex2);
                }
            }
        }

        private static void thContext(object obj)
        {
            HttpListenerContext context = (HttpListenerContext)obj;
            HttpListenerRequest request = context.Request;
            SortedList<string, string> slQuery = null;
            SortedList<string, byte[]> slData = new SortedList<string,byte[]>();
            SortedList<string, string> slCookie = ReadCookies(request.Cookies);

            //Console.WriteLine(request.RawUrl);


            if (request.HttpMethod.ToLower() == "get")
            {
                slQuery = ReadUrl(request.RawUrl);
            }
            else if (request.HttpMethod.ToLower() == "post")
            {

                if (request.ContentType != null && request.ContentType.Contains("multipart/form-data;"))
                {
                    slQuery = ReadUrl(request.RawUrl);
                    try
                    {
                        string FileName, FileType, TempName;
                        byte[] FileBody;
                        FileBody = ReadPostStream(request, out FileName, out FileType, out TempName);

                        slQuery.Add("FileName", FileName);
                        slQuery.Add("FileType", FileType);
                        slQuery.Add("TempName", TempName);

                        slData.Add(TempName, FileBody);
                    }
                    catch (Exception ex)
                    {
                        slQuery.Add("Error", ex.Message);
                    }

                }
                else
                {
                    string postData = ReadPostString(request.InputStream, request.ContentEncoding);
                    slQuery = ReadUrl(request.RawUrl + "&" + postData);
                }
                request.InputStream.Close();
            }

            if (ReadContentType(request.RawUrl) != "text/html" && slQuery == null)
                SendHtmlByUrl(context.Response, request.RawUrl);
            else if (RequestEvent != null)
                RequestEvent(request.RemoteEndPoint.Address.ToString(), context.Response,request.UserHostAddress, request.RawUrl, slCookie, slQuery,slData);
            else
                SendHtmlByUrl(context.Response, request.RawUrl);

        }

        public static SortedList<string, string> ReadCookies(CookieCollection Cookies)
        {
            SortedList<string, string> slCookie = new SortedList<string, string>();

            foreach (Cookie c in Cookies)
            {
                if (!slCookie.ContainsKey(c.Name))
                    slCookie.Add(ToolsHelper.UrlToStr(c.Name), ToolsHelper.UrlToStr(c.Value));
            }
            return slCookie;
        }

        public static SortedList<string, string> ReadUrl(string url)
        {

            if (!url.Contains("?"))
                return null;


            url = url.Split('?')[1];
            return ReadField(url + "&");
        }

        public static SortedList<string, string> ReadField(string url)
        {
            SortedList<string, string> sl = new SortedList<string, string>();
            string[] fields = (url + "&").Split('&');
            for (int i = 0; i < (fields.Length - 1); i++)
            {
                if (fields[i].Contains("="))
                {
                    int firstSplit = fields[i].IndexOf('=');

                    //没有值，如xx=
                    if (fields[i].Length == (firstSplit + 1))
                        continue;

                    string key = fields[i].Substring(0, firstSplit);
                    string value = fields[i].Substring(firstSplit + 1, fields[i].Length - firstSplit - 1);

                    if (sl.ContainsKey(key))
                        sl[key] = ToolsHelper.UrlToStr(value);
                    else
                        sl.Add(key, ToolsHelper.UrlToStr(value));
                }
            }

            return sl;
        }

        private static string ReadPostString(Stream inputStream, Encoding encoding)
        {
            StreamReader reader = new StreamReader(inputStream, encoding);
            string data = reader.ReadToEnd();
            reader.Dispose();
            return data;
        }

        private static byte[] ReadPostStream(string FilePath, Stream inputStream, int count)
        {
            FileStream fs = new FileStream(FilePath, FileMode.OpenOrCreate);
            BinaryReader br = new BinaryReader(inputStream);

            try
            {
                int readSize = 8192;
                int splitCount = count / readSize;
                int fixCount = count % readSize;
                byte[] buffer = new byte[readSize];

                while (splitCount-- > 0)
                {
                    br.Read(buffer, 0, readSize);
                    fs.Write(buffer, 0, buffer.Length);
                }

                if (fixCount > 0)
                {
                    buffer = new byte[fixCount];
                    br.Read(buffer, 0, fixCount);
                    fs.Write(buffer, 0, fixCount);
                }


            }
            catch (Exception ex)
            {

            }
            finally
            {
                //br.Dispose();
                fs.Dispose();
            }

            if (File.Exists(FilePath))
                return File.ReadAllBytes(FilePath);

            return null;
        }


        private static byte[] ReadPostStream(HttpListenerRequest request, out string FileName, out string FileType, out string TempName)
        {
            //2018.11.12 使用诺瓦PSD100，修改为2.0后，不能用Skip(1)
            //string[] values = request.ContentType.Split(';').Skip(1).ToArray();
            //string[] values = request.ContentType.Split(';');
            //string Boundary = string.Join(";", values).Replace("boundary=", "").Trim();
            string Boundary = request.ContentType.Replace("multipart/form-data; boundary=", "").Trim();
            byte[] EndBoundary = Encoding.UTF8.GetBytes("--" + Boundary + "--\r\n");

            string bodyString = "";
            int readCount = 0;
            int endCount = (int)request.ContentLength64 - EndBoundary.Length;

            while (true)
            {
                byte[] buffer = ReadLineAsBytes(request.InputStream);
                readCount += buffer.Length;

                if (Encoding.UTF8.GetString(buffer).Equals("\r\n"))
                    break;

                bodyString += Encoding.UTF8.GetString(buffer);
            }
            Match mc = Regex.Match(bodyString, "filename=\"(.*?)\"");

            if (mc.Groups[1].Value.Contains("\\"))
            {
                mc = Regex.Match(mc.Groups[1].Value, @".*\\([^\.]+\..{3})");
            }

            if (mc.Success && mc.Groups[1].Value.Contains("."))
            {
                string[] Ext = mc.Groups[1].Value.Split('.');

                //文件名称
                FileName=Ext[0];
                FileType = Ext[1].ToLower();

                TempName = DateTime.Now.ToString("yyMMddHH_mmssffff") + "." + FileType;  //使用文件名会存在乱码的问题，string.Format("{0}{1}{2}_{3}", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, mc.Groups[1].Value);

                return ReadPostStream(TempFolder + TempName, request.InputStream, endCount - readCount - 2); 
            }

            FileName = "";
            FileType = "";
            TempName = "";
            return null;
        }

        private static byte[] ReadLineAsBytes(Stream SourceStream)
        {
            var resultStream = new MemoryStream();
            while (true)
            {
                int data = SourceStream.ReadByte();
                resultStream.WriteByte((byte)data);
                if (data == 10)
                    break;
            }

            resultStream.Position = 0;
            byte[] dataBytes = new byte[resultStream.Length];
            resultStream.Read(dataBytes, 0, dataBytes.Length);

            return dataBytes;
        }


        public static string ReadContentType(string url)
        {
            if (url.Contains("."))
            {
                string[] exts = url.ToLower().Split('.');
                string ext=exts[exts.Length-1];
                if (_headContentType.ContainsKey(ext))
                    return _headContentType[ext];
            }

            return "text/html";
        }

        public static string GetFileFullName(string url, bool isDisk)
        {
            url = url.Substring(url.Length - 1, 1) == "/" ? "/" + DefaultPage : url;
            url = isDisk ? RootFolder + url.Replace("/", "\\") : RootFolderStream + url.Replace("/", ".");
            return url;
        }

        public static string GetFileAndSendHtml(string url, Hashtable keyValues = null)
        {
            string html = null;

            if (File.Exists(GetFileFullName(url, true)))
            {
                html = GetFileInDiskHtml(url, keyValues);
                if (html != null)
                    return html;
            }

            if (html == null && RootFolderStream != null)
            {
                html = GetFileInStream(url, keyValues);
                if (html != null)
                    return html;
            }


            //2019.7.5 修改为首先读取本地磁盘的，这样临时有变动的可以随时修改
            //if (RootFolderStream != null)
            //{
            //    html = GetFileInStream(url, keyValues);
            //    if (html != null)
            //        return html;
            //}

            //if (html == null)
            //{
            //    if (File.Exists(GetFileFullName(url, true)))
            //        return GetFileInDiskHtml(url, keyValues);
            //}

            return "";
        }

        public static string GetFileInStream(string url, Hashtable keyValues = null)
        {
            string fullname = GetFileFullName(url.ToLower(), false);
            Stream stream = AssemblyGet.GetManifestResourceStream(fullname);    //.GetExecutingAssembly()
           
            if (stream == null)
                return null;

            StreamReader sr = new StreamReader(stream, Encoding.UTF8);
            string ret = sr.ReadToEnd();
            sr.Close();

            return GetFileAnalyze(ret, keyValues);
        }

        public static string GetFileInDiskHtml(string url, Hashtable keyValues = null)
        {
            string fullname = GetFileFullName(url, true);
            if (!File.Exists(fullname))
                return null;

            StreamReader sr = new StreamReader(fullname, Encoding.UTF8);
            string ret = sr.ReadToEnd();
            sr.Close();

            return GetFileAnalyze(ret, keyValues);

        }

        private static string GetFileAnalyze(string html, Hashtable keyValues = null)
        {

            //如果网页里没有变量符号，则返回原文
            if (!html.Contains(analyzeLeft))
                return html;

            try
            {

                Regex reg = new Regex(analyzeLeft + @"include\(""(.*?)""\)" + analyzeRight);
                foreach (Match mc in reg.Matches(html))
                {
                    string file = GetFileAndSendHtml(mc.Groups[1].Value);
                    if (file != null)
                    {
                        html =html.Replace(mc.Groups[0].Value, file);
                    }
                }

                if (StaticDefine.Count > 0)
                {
                    reg = new Regex(analyzeLeft + @"define\(""(.*?)""\)" + analyzeRight);
                    foreach (Match mc in reg.Matches(html))
                    {
                        string key = mc.Groups[1].Value;
                        if(StaticDefine.ContainsKey(key))
                        {
                            html = html.Replace(mc.Groups[0].Value, StaticDefine[key].ToString());
                        }
                    }
                }
                if (keyValues != null)
                {
                    foreach (string key in keyValues.Keys)
                        html = html.Replace(analyzeLeft + key + analyzeRight, keyValues[key].ToString());
                }
            }
            catch (Exception ex)
            {
                LogHelper.Err("Helper.Http.HttpServerHelper.cs", "GetFileAnalyze()", ex);
            }


            return html;
        }



        public static void SendHtmlByUrl(HttpListenerResponse response, string url, Hashtable keyValues = null)
        {
            url = url.Contains("?") ? url.Split('?')[0] : url;
            string FullName = GetFileFullName(url, true);
            //非网页文件
            if (ReadContentType(url) != "text/html" && File.Exists(FullName))
                SendByte(response, ReadContentType(url), File.ReadAllBytes(FullName));
            else  //是网页文档
                SendHtml(response, GetFileAndSendHtml(url, keyValues));
        }

        public static void SendHtml(HttpListenerResponse response, string responseString)
        {
            SendByte(response, "text/html", Encoding.UTF8.GetBytes(responseString));
        }

        public static void SendByte(HttpListenerResponse response, string ContentType, byte[] data)
        {
            try
            {
                if (data != null)
                {

                    response.ContentLength64 = data.Length;
                    response.ContentType = ContentType;
                    response.OutputStream.Write(data, 0, data.Length);
                    response.OutputStream.Flush();
                }
                response.OutputStream.Close();

            }
            catch (Exception ex)
            {
                //LogHelper.Err("RT.Helper.Http.HttpServerHelper.cs", "SendByte()", ex);
            }
        }

        public static void SendFile(HttpListenerResponse response, string FileName,byte[] data)
        {
            try
            {
                 if (data != null)
                {
                    string ContentType = ReadContentType(FileName);
                    response.Headers.Add("Content-Disposition:attachment;filename=" + FileName);
                    response.ContentEncoding = Encoding.GetEncoding("gb2312");

                    response.ContentLength64 = data.Length;
                    response.ContentType = ContentType;

                    response.OutputStream.Write(data, 0, data.Length);
                    response.OutputStream.Flush();
                }
                else
                {
                    //LogHelper.Err("RT.Helper.Http.HttpServerHelper.cs", "SendFile()","数据为空：" + FileName);
                }
                response.OutputStream.Close();

            }
            catch (Exception ex)
            {
                //LogHelper.Err("RT.Helper.Http.HttpServerHelper.cs", "SendFile()", ex);
            }
        }



        public static void SendFile(HttpListenerResponse response, string FilePath)
        {
            try
            {
                FileInfo file = new FileInfo(FilePath);
                if (!file.Exists)
                {
                    SendHtml(response, "没有找到该文件!(No found this file!)");
                    return;
                }
                byte[] data = File.ReadAllBytes(FilePath);

                if (data != null)
                {
                    string ContentType = ReadContentType(FilePath);
                    response.Headers.Add("Content-Disposition:attachment;filename=" + file.Name);
                    response.ContentLength64 = data.Length;
                    response.ContentType = ContentType;

                    response.OutputStream.Write(data, 0, data.Length);
                    response.OutputStream.Flush();
                }
                else
                {
                    //LogHelper.Err("RT.Helper.Http.HttpServerHelper.cs", "SendFile()", "文件内容为空：" + FilePath);
                }
                response.OutputStream.Close();

            }
            catch (Exception ex)
            {
                //LogHelper.Err("RT.Helper.Http.HttpServerHelper.cs", "SendFile()", ex);
            }
        }

    }
}
